package com.example.netcamerastreamdemo;

import android.content.Context;
import android.media.MediaDataSource;
import android.os.Build;
import android.util.Log;

import androidx.annotation.RequiresApi;

import android.view.SurfaceView;
import androidx.annotation.NonNull;
import com.google.gson.Gson;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Hashtable;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;

@RequiresApi(api = Build.VERSION_CODES.M)
public class StreamMediaDataSource extends MediaDataSource {
    private static final String TAG = "StreamMediaDataSource";
    //    private static final String STREAM_START_FLAG = "VSDK_CMD_TX_RT_VIDEO_START";
    private static final String STREAM_START_FLAG = "PLAY_FLV";
    private static final String STREAM_STOP_FLAG = "VSDK_CMD_TX_RT_VIDEO_STOP";
    private static final String STREAM_HEART_BEAT_FLAG = "HB";
    private static final int TIMEOUT_VALUE = 30;
    private WebSocket mWebSocket;
    private Gson mGson = new Gson();
    private Hashtable<Integer, byte[]> mStreamData = new Hashtable<>();
    private int mWriteIndex = -1;
    private SurfaceView mSurfaceView;
    private String mWebsocketUrl;
    private VideoStreamPlayer.MsgData mAuthData;
    private boolean isPlaying = false;
    private boolean isWebsocketConnected = false;
    private int mReadIndex = 0;
    private StreamCallback mStreamCallback;
    private int mUnfinishFrameCount = 0;
    private int mDataLength = 0;
//    File file;
//    FileOutputStream fileOutputStream;
    Context mContext;

    public StreamMediaDataSource(
            Context context,
            @NonNull SurfaceView mSurfaceView,
            @NonNull String url,
            @NonNull VideoStreamPlayer.MsgData authData) {
        this.mContext = context;
        this.mSurfaceView = mSurfaceView;
        this.mWebsocketUrl = url;
        this.mAuthData = authData;
    }

    public void setmStreamCallback(StreamCallback streamCallback) {
        this.mStreamCallback = streamCallback;
    }

    @Override
    public int readAt(long position, byte[] bytes, int offset, int size) throws IOException {
//        Log.i(TAG, "---readAt===");
//        return 0;

        Log.e(TAG, "---数据索引===" + mReadIndex);
        Log.i(TAG, "---readAt==起始点=" + position);
        Log.i(TAG, "---readAt==可用长度=" + size);
//        try {
//            Thread.sleep(100);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        if (null == mStreamData.get(mReadIndex)){
            Log.i(TAG, "---无数据===");
            return 0;
        }else {

            byte[] b = mStreamData.get(mReadIndex).clone();
            int len = b.length;
            Log.i(TAG, "---取到数据===" + len);
            if (len > size){
                Log.i(TAG, "---本帧索引===" + mUnfinishFrameCount);
                if ((len - mUnfinishFrameCount) > size){
                    System.arraycopy(b, mUnfinishFrameCount, bytes, (int) position, size);
                    mUnfinishFrameCount = size + mUnfinishFrameCount;

                    Log.i(TAG, "---放入大型数据===" + size);
                    mDataLength = mDataLength + size;
                    return size;
                }else {
                    System.arraycopy(b, mUnfinishFrameCount, bytes, (int) position, len - mUnfinishFrameCount);
                    mReadIndex++;
                    Log.i(TAG, "---放入收尾数据===" + (len - mUnfinishFrameCount));
                    mDataLength = mDataLength + (len - mUnfinishFrameCount);
                    return len - mUnfinishFrameCount;
                }

            }else {
                mUnfinishFrameCount = 0;
                Log.i(TAG, "---放入小型数据===" + len);
                System.arraycopy(b, 0, bytes, (int) position, len);
                mReadIndex++;
                mDataLength = mDataLength + len;
                return len;
            }}
    }

    @Override
    public long getSize() throws IOException {
        Log.i(TAG, "---getSize===" + mDataLength);
        return mDataLength;
    }

    @Override
    public void close() throws IOException {
        Log.i(TAG, "---close===");

    }

    public boolean prepare(){
        if (mSurfaceView == null){
            return false;
        }
        if (mWebsocketUrl.isEmpty()){
            return false;
        }
        if (mAuthData == null){
            return false;
        }
        initWebSocket();
//        file = new File(mContext.getExternalFilesDir(null) + "/" + System.currentTimeMillis() + ".flv");
//        try{
//            fileOutputStream = new FileOutputStream(file);
//        } catch (FileNotFoundException e) {
//            e.printStackTrace();
//        }
        return true;
    }

    public void startPlay(){
        if (isPlaying){
            return;
        }
        isPlaying = true;
        sendStartMsg();
    }

    public void stopPlay(){
        if (!isPlaying){
            return;
        }
        reset();
        sendStopVideoMsg();
    }

    public void release(){
        mWebSocket.cancel();
    }

    private void reset(){
//        try {
//            fileOutputStream.close();
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
        isPlaying = false;
        mStreamData.clear();
        mWriteIndex = -1;
    }

    private void sendStartMsg() {
        if (isWebsocketConnected) {
            VideoStreamPlayer.MsgData startVideoMsg = new VideoStreamPlayer.MsgData(
                    STREAM_START_FLAG,
                    mAuthData.dev_guid,
                    mAuthData.video_chnn,
                    mAuthData.sub_chnn,
                    mAuthData.rec_path);
            mWebSocket.send(mGson.toJson(startVideoMsg));
        }
    }

    private void sendStopVideoMsg(){
        if (isWebsocketConnected) {
            VideoStreamPlayer.MsgData stopVideoMsg = new VideoStreamPlayer.MsgData(
                    STREAM_STOP_FLAG,
                    mAuthData.dev_guid,
                    mAuthData.video_chnn,
                    mAuthData.sub_chnn);
            mWebSocket.send(mGson.toJson(stopVideoMsg));
        }
    }

    private void sendHeartBeatMsg(){
        if (isWebsocketConnected) {
            VideoStreamPlayer.MsgData heartBeatMsg = new VideoStreamPlayer.MsgData();
            heartBeatMsg.cmd = STREAM_HEART_BEAT_FLAG;
            mWebSocket.send(mGson.toJson(heartBeatMsg));
        }
    }

    private void initWebSocket(){
        OkHttpClient mClient = new OkHttpClient.Builder()
                .readTimeout(TIMEOUT_VALUE, TimeUnit.SECONDS)//设置读取超时时间
                .writeTimeout(TIMEOUT_VALUE, TimeUnit.SECONDS)//设置写的超时时间
                .connectTimeout(TIMEOUT_VALUE, TimeUnit.SECONDS)//设置连接超时时间
                .build();

        Request request = new Request.Builder().get().url(mWebsocketUrl).build();
        mWebSocket = mClient.newWebSocket(request, new WebSocketListener() {
            @Override
            public void onOpen(WebSocket webSocket, Response response) {
                super.onOpen(webSocket, response);
                Log.i(TAG, "---onOpen===");
                isWebsocketConnected = true;
            }

            @Override
            public void onMessage(WebSocket webSocket, String text) {
                super.onMessage(webSocket, text);
                Log.i(TAG, "---onMessage===" + text);
                if (text.contains(STREAM_START_FLAG)){
//                    mStreamStartTime = System.currentTimeMillis();
                }
            }

            @Override
            public void onMessage(WebSocket webSocket, ByteString bytes) {
                super.onMessage(webSocket, bytes);
                Log.i(TAG, "---onMessage===" + (bytes.size()));
                mWriteIndex++;
//                mStreamData.put(mWriteIndex, bytes.substring(44).toByteArray());
                mStreamData.put(mWriteIndex, bytes.toByteArray());
                if (mWriteIndex == 10){
                    mStreamCallback.streamIsStart();
                }
//                try {
//                    fileOutputStream.write(bytes.toByteArray());
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
            }

            @Override
            public void onClosing(WebSocket webSocket, int code, String reason) {
                super.onClosing(webSocket, code, reason);
                Log.i(TAG, "---onClosing===" + reason);
                isWebsocketConnected = false;
            }

            @Override
            public void onClosed(WebSocket webSocket, int code, String reason) {
                super.onClosed(webSocket, code, reason);
                Log.i(TAG, "---onClosed===" + reason);
                isWebsocketConnected = false;
            }

            @Override
            public void onFailure(WebSocket webSocket, Throwable t, Response response) {
                super.onFailure(webSocket, t, response);
                Log.i(TAG, "---onFailure===" + t.toString());
                isWebsocketConnected = false;
                initWebSocket();
            }
        });
    }

    interface StreamCallback{
        void streamIsStart();
    }
}
